#pragma once
#include <fstream> // ReadFile和WriteFile里用到
#include <string> // ReadFile和WriteFile的入参是std::string类型
#include <sys/stat.h> // ReadFile和WriteFile里用到
#include <fcntl.h> // 提示WriteFile里面的O_RDWR等找不到，添加该头文件
#include <unistd.h> // unix下头文件 提供read、write等的原型

#include <cstdio> // 标准输入输出 printf fprintf
#include <algorithm> //std::abs,min,max等算法
#include "acl/acl.h"
#include "kernel_operator.h"


// 日志输出宏
#define INFO_LOG(fmt, args...) fprintf(stdout, "[INFO] " fmt "\n", __LINE__, __func__, __FILE__, ##args)
#define WARN_LOG(fmt, args...) fprintf(stdout, "[WARN] ( at line %d in func %s in file %s ) " fmt "\n", __LINE__, __func__, __FILE__, ##args)
#define ERROR_LOG(fmt, args...) fprintf(stdout, "[ERROR] ( at line %d in func %s in file %s ) " fmt "\n", __LINE__, __func__, __FILE__, ##args)

// acl函数（NPU侧kernel直调相关函数）检查宏，传入的x是acl函数
// 这里的do while(0)是为了在代码块中定义变量接收传入的x
#define ACL_CHECK(x)                                \
    do{                                             \
        auto ret = (x);                             \
        if ( ret != ACL_SUCCESS ) {                 \
            ERROR_LOG("ACL_CHECK failed: "#x);      \
            exit(ret);                               \
        } else {                                    \
            /*INFO_LOG("ACL_CHECK succeeded: "#x);*/     \
        }                                           \
    }while( 0 ) 
    

/**
 * @brief Read data from file （该文件来自Ascend C 官方samples）
 * @param [in] filePath: file path
 * @param [in] buffer: file buffer for data
 * @param [out] fileSize: file (buffer) size
 * @return read result succeed or not
 */
bool ReadFile(const std::string &filePath, void *buffer, size_t bufferSize)
{
    struct stat sBuf;
    int fileStatus = stat(filePath.data(), &sBuf);
    if (fileStatus == -1) {
        ERROR_LOG("%s Failed to get file.", filePath.c_str());
        return false;
    }
    if (S_ISREG(sBuf.st_mode) == 0) {
        ERROR_LOG("%s is not a file, please enter a file", filePath.c_str());
        return false;
    }

    std::ifstream file;
    file.open(filePath, std::ios::binary);
    if (!file.is_open()) {
        ERROR_LOG("Open file failed. path = %s", filePath.c_str());
        return false;
    }

    std::filebuf *buf = file.rdbuf();
    size_t size = buf->pubseekoff(0, std::ios::end, std::ios::in);
    if (size == 0) {
        ERROR_LOG("file size is 0");
        file.close();
        return false;
    }
    if (size != bufferSize) {
        ERROR_LOG("file size is not equal buffer size, file size: %zu, while buffer size: %zu", size, bufferSize);
        file.close();
        return false;
    }
    buf->pubseekpos(0, std::ios::in);
    buf->sgetn(static_cast<char *>(buffer), size);
    file.close();
    return true;
}

/**
 * @brief Write data to file （该文件来自Ascend C 官方samples）
 * @param [in] filePath: file path
 * @param [in] buffer: data to write to file
 * @param [in] size: size to write
 * @return write result
 */
bool WriteFile(const std::string &filePath, const void *buffer, size_t size)
{
    if (buffer == nullptr) {
        ERROR_LOG("Write file failed. buffer is nullptr");
        return false;
    }

    int fd = open(filePath.c_str(), O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWRITE);
    if (fd < 0) {
        ERROR_LOG("Open file failed. path = %s", filePath.c_str());
        return false;
    }

    size_t writeSize = write(fd, buffer, size);
    (void)close(fd);
    if (writeSize != size) {
        ERROR_LOG("Write file Failed.");
        return false;
    }

    return true;
}

/**
 * @brief 
 比较输出结果与期望结果，输出错误指标：
    · max_abs_error: 最大绝对误差
    · max_rel_error: 最大相对误差
    · avg_abs_error: 平均绝对误差
    · avg_rel_error: 平均相对误差
    · errorCount: 相对误差或相对误差超过thres（阈值）的数据个数（理想状态应该为0）
    · errorCountRatio: 相对误差或相对误差超过thres的数据在全部数据中的比例
    · avg_eb: 均衡误差性，看整体结果偏大或偏小
 计算方法：
    · 若计算结果actual，真值为expect
    · 绝对误差: abs_error = abs( actual - expect )
    · 相对误差：rel_error = abs_error / abs( expect );
    · 如何统计errorCount：在 用更小的误差来与 thres 比较， 进一步区分 [REL ERROR] 和 [ABS ERROR]
        当 expect > 1 时，有 abs_error > rel_error，此时 rel_error > thres 则为[REL ERROR]
        否则 rel_error > abs_error，此时 abs_error > thres 则为[ABS_ERROR]
    · 均衡误差性？ ( ( actual[i] - expect[i] ) / max( expect , 1.0 ) ) / len
        这个注意这个是有负数的，没有取绝对值。expect与1取max是防止整个值太大。
        最后均衡误差大于eb_thres时为[EB ERROR]，因为理想数据应该是无偏的
 * @param [in] outputData: 算子输出结果
 * @param [in] expectData: 脚本计算的期望结果
 * @param [in] len: 数据总长（当作一维数据来计算）
 * @param [in] calc_times: 用于确定允许误差的范围（外面传入K）
 * @return: None
 */
template<typename T>
void calOutputError( T *outputData, T *expectData, uint64_t len, int64_t zeroPaddingK, int64_t zeroPaddingM, int64_t zeroPaddingN ) {
    // 根据 T 设定阈值
    double error_threshold, eb_threshold;
    if (std::is_same<T, float>::value) {
        if (zeroPaddingK < 2048)
            error_threshold = 1.0f/(1<<11);
        else if (zeroPaddingK < 16384)
            error_threshold = 1.0f/(1<<10);
        else
            error_threshold = 1.0f/(1<<9);
        eb_threshold = 1.0f/(1<<14);
    } else if (std::is_same<T, half>::value) {
        if (zeroPaddingK < 2048)
            error_threshold = 1.0f/(1<<8);
        else
            error_threshold = 1.0f/(1<<7);
        eb_threshold = 1.0f/(1<<10);
    }
    double max_abs_error = 0.0f;
    double max_rel_error = 0.0f;
    double sum_abs_error = 0.0f;
    double sum_rel_error = 0.0f;
    uint64_t errorCount = 0;
    double sum_eb = 0.0;

    bool errorCountWithin16 = true;

    for( uint64_t i = 0; i < len; i++ ) {
        // 当前数据
        T cur_output_data = outputData[i];
        T cur_expect_data = expectData[i];

        // 当前数据所在矩阵+矩阵下标
        uint64_t singleCStride = zeroPaddingM * zeroPaddingN;
        uint64_t batchNum = i/singleCStride;
        uint64_t indexM = i%singleCStride / zeroPaddingN;
        uint64_t indexN = i%singleCStride % zeroPaddingN;

        // 当前双误差
        // 注意这里，std::abs内没有强转会提示abs有歧义
        double cur_abs_error = std::abs( (double)cur_output_data - (double)cur_expect_data );
        double cur_rel_error = cur_abs_error / ( std::abs( (double)cur_expect_data ) + 1e-7 );// 这里cur_expect_data可能是0（脚本问题），加上一个极小值使得结果不出错
        
        // 分别更新最大绝对、相对误差
        if ( cur_abs_error > max_abs_error ) {
            // WARN_LOG("Cur max abs error %lf at batch %ld (%ld, %ld). "
            //         "Current data  %lf and expect data  %lf while error threshold %lf", 
            //         cur_abs_error, batchNum, indexM, indexN, 
            //         cur_output_data, cur_expect_data, error_threshold);
            max_abs_error = cur_abs_error;
        }
        if ( cur_rel_error > max_rel_error ) {
            // WARN_LOG("Cur max rel error %lf at batch %ld (%ld, %ld). "
            //         "Current data  %lf and expect data  %lf while error threshold %lf", 
            //         cur_rel_error, batchNum, indexM, indexN, 
            //         cur_output_data, cur_expect_data, error_threshold);
            max_rel_error = cur_rel_error;
        }

        // 累计总绝对、相对误差
        sum_abs_error += cur_abs_error;
        sum_rel_error += cur_rel_error;
        sum_eb += ( cur_output_data - cur_expect_data ) / std::max( std::abs( (double)cur_expect_data), 1.0 );
        
        // 判断双误差是否超过阈值
        if( std::abs((double)cur_expect_data)>=1 ){ // cur_abs_error >= cur_rel_error
            if(cur_rel_error > error_threshold){
                ++errorCount;
                if(errorCount < 16){
                    ERROR_LOG("Rel error %lf at batch %ld index (%ld, %ld). "
                            "Current data %lf and expect data %lf while error threshold %lf. ", 
                            cur_rel_error, batchNum, indexM, indexN, 
                            cur_output_data, cur_expect_data, error_threshold);
                }else{
                    if( errorCountWithin16 ){
                        ERROR_LOG("Error num exceeds 16. Stop printing error infos.");
                        errorCountWithin16 = false;
                    }
                }
            }
        }else{
            if(cur_abs_error > error_threshold){ // cur_abs_error < cur_rel_error
                ++errorCount;
                if(errorCount < 16){
                    ERROR_LOG("Abs error %lf at batch %ld index (%ld, %ld). "
                            "Current data %lf and expect data %lf while error threshold %lf. ", 
                            cur_abs_error, batchNum, indexM, indexN, 
                            cur_output_data, cur_expect_data, error_threshold);
                }else{
                    if( errorCountWithin16 ){
                        ERROR_LOG("Error num exceeds 16. Stop printing error infos.");
                        errorCountWithin16 = false;
                    }
                }
            }
        }
    }

    double avg_abs_error = sum_abs_error / (double)len;
    double avg_rel_error = sum_rel_error / (double)len;
    double errorCountRatio = (double)errorCount / (double)len;
    double avg_eb = sum_eb / (double)len;
    
    if( std::abs( avg_eb ) > eb_threshold ) {
        ERROR_LOG("Eb error %lf while eb threshold %lf. ", avg_eb, eb_threshold);
    }
    printf("===================================\n"
            "Output error statistics: \n"
            "===================================\n"
            // "max_abs_error = %lf, \n"
            // "max_rel_error = %lf, \n"
            // "avg_abs_error = %lf, \n"
            // "avg_rel_error = %lf, \n"
            "errorCount = %ld, \n"
            "errorCountRatio = %lf \n"
            // "avg_eb = %lf. \n"
            "===================================\n\n",
            // max_abs_error, 
            // max_rel_error, 
            // avg_abs_error, 
            // avg_rel_error, 
            errorCount, 
            errorCountRatio
            // , avg_eb
            );
}

/**
 * @brief val 向上按照 align 的倍数取整
 * @param [in] T val：需要取整的数
 * @param [in] align：需要向上取整的最小粒度
 * @return T 向上对齐后的值
 */
template<typename T>
T RoundUp (const T val, const T align) {
    if ( align == 0 ) {
        return 0;
    } else {
        return ( val + align - 1 ) / align * align ;
    }
}



/**
 * @brief val 向下按照 align 的倍数取整
 * @param [in] T val：需要取整的数
 * @param [in] T align：需要向下取整的最小粒度
 * @return T 向下对齐后的数
 */
template<typename T>
T RoundDown ( const T val, const T align ) {
    if ( align == 0 ) {
        return 0;
    } else {
        return val / align * align;
    }
}

/**
 * @brief val 除以 divisor 向上取整 
 * @param [in] T val：被除数
 * @param [in] T divisor：除数
 * @return T val除以divisor向上取整的结果
 */
template<typename T>
T CeilDiv ( const T val, const T divisor ){
    if ( divisor == 0 ) {
        return 0;
    } else {
        return ( val + divisor - 1 ) / divisor;
    }

}



